package com.niodata.dp.monitor.core.task.util;

import java.io.IOException;
import java.net.URI;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;

public class HttpCaller {

  /**
   * http get.
   *
   * @param type return type
   * @param url url
   * @param queryParam query params
   */
  public static <T> T get(Class<T> type, String url,
        Map<String, String> queryParam, Map<String, String> headers)
        throws IOException {
    return get(type, url, queryParam, headers, 15, null);
  }

  /**
   * http get.
   *
   * @param clazz return type
   * @param url url
   * @param queryParam query params
   * @param timeout socket timeout
   * @param <T> return type
   * @return return object
   */
  @SuppressWarnings("unchecked")
  public static <T> T get(Class<T> clazz, String url,
        Map<String, String> queryParam, Map<String, String> headers,
        int timeout, HttpHost proxy) throws IOException {
    RequestConfig.Builder requestConfigBuilder = RequestConfig.custom()
          .setConnectTimeout(10000).setSocketTimeout(timeout * 1000);
    if (proxy != null) {
      requestConfigBuilder.setProxy(proxy);
    }

    CloseableHttpClient client = HttpClientBuilder.create()
          .setDefaultRequestConfig(requestConfigBuilder.build()).build();
    HttpGet get = new HttpGet(url);
    if (headers != null && headers.size() > 0) {
      for (String key : headers.keySet()) {
        get.setHeader(key, headers.get(key));
      }
    }
    try {
      if (queryParam != null && queryParam.size() > 0) {
        URIBuilder builder = new URIBuilder(get.getURI());
        for (String key : queryParam.keySet()) {
          builder.addParameter(key, queryParam.get(key));
        }
        get.setURI(builder.build());
      }

    } catch (Exception exception) {
      exception.printStackTrace();

    }
    HttpEntity entity = null;
    try {
      HttpResponse response = client.execute(get);
      int statusCode = response.getStatusLine().getStatusCode() / 100;
      entity = response.getEntity();
      String result = EntityUtils.toString(response.getEntity());
      if (statusCode == 4 || statusCode == 5) {
        throw new IOException(result);
      }

      if (String.class.equals(clazz)) {
        return (T) result;
      }
      if (result == null || result.length() == 0) {
        return null;
      }

      return JsonUtil.fromJson(clazz, result);
    } finally {
      if (entity != null) {
        EntityUtils.consume(entity);
      }
    }
  }

  public static <T> T post(Class<T> clazz, String url,
        Map<String, String> queryParam, String body,
        Map<String, String> headers) throws IOException {
    return post(clazz, url, queryParam, headers, body, 15, null);
  }

  /**
   * http post.
   *
   * @param clazz return type
   * @param url request url
   * @param queryParam query params
   * @param body request body
   * @param timeout time out seconds
   * @param <T> return type
   * @return return object
   */
  @SuppressWarnings("unchecked")
  public static <T> T post(Class<T> clazz, String url,
        Map<String, String> queryParam, Map<String, String> headers,
        String body, int timeout, HttpHost proxy) throws IOException {

    RequestConfig.Builder requestConfigBuilder = RequestConfig.custom()
          .setConnectTimeout(10000).setSocketTimeout(timeout * 1000);
    if (proxy != null) {
      requestConfigBuilder.setProxy(proxy);
    }
    CloseableHttpClient client = HttpClientBuilder.create()
          .setDefaultRequestConfig(requestConfigBuilder.build()).build();

    HttpPost post = new HttpPost(url);
    if (headers != null && headers.size() > 0) {
      for (String key : headers.keySet()) {
        post.setHeader(key, headers.get(key));
      }
    }
    try {
      if (queryParam != null && queryParam.size() > 0) {
        URIBuilder builder = new URIBuilder(post.getURI());
        for (String key : queryParam.keySet()) {
          builder.addParameter(key, queryParam.get(key));
        }
        post.setURI(builder.build());
      }
    } catch (Exception exception) {
      exception.printStackTrace();
    }

    if (body != null) {
      HttpEntity entity = new StringEntity(body,
            ContentType.APPLICATION_JSON);
      post.setEntity(entity);
    }
    HttpEntity entity = null;
    try {
      HttpResponse response = client.execute(post);
      int statusCode = response.getStatusLine().getStatusCode() / 100;
      entity = response.getEntity();
      String result = EntityUtils.toString(response.getEntity());
      if (statusCode == 4 || statusCode == 5) {
        throw new IOException(result);
      }
      if (String.class.equals(clazz)) {
        return (T) result;
      }
      if (result == null || result.length() == 0) {
        return null;
      }

      return JsonUtil.fromJson(clazz, result);
    } finally {
      if (entity != null) {
        EntityUtils.consume(entity);
      }
    }
  }

  /**
   * http delete.
   *
   * @param type return type
   * @param url url
   * @param queryParam query params
   */
  public static <T> T delete(Class<T> type, String url,
        Map<String, String> queryParam, String body, Map<String, String> headers)
        throws IOException {
    return delete(type, url, queryParam, headers, body, 15, null);
  }

  /**
   * http delete.
   *
   * @param clazz return type
   * @param url url
   * @param queryParam query params
   * @param timeout socket timeout
   * @param <T> return type
   * @return return object
   */
  @SuppressWarnings("unchecked")
  public static <T> T delete(Class<T> clazz, String url,
        Map<String, String> queryParam, Map<String, String> headers,
        String body, int timeout, HttpHost proxy) throws IOException {
    RequestConfig.Builder requestConfigBuilder = RequestConfig.custom()
          .setConnectTimeout(10000).setSocketTimeout(timeout * 1000);
    if (proxy != null) {
      requestConfigBuilder.setProxy(proxy);
    }
    CloseableHttpClient client = HttpClientBuilder.create()
          .setDefaultRequestConfig(requestConfigBuilder.build()).build();
    DpHttpDelete delete = new DpHttpDelete(url);
    if (headers != null && headers.size() > 0) {
      for (String key : headers.keySet()) {
        delete.setHeader(key, headers.get(key));
      }
    }
    try {
      if (queryParam != null && queryParam.size() > 0) {
        URIBuilder builder = new URIBuilder(delete.getURI());
        for (String key : queryParam.keySet()) {
          builder.addParameter(key, queryParam.get(key));
        }
        delete.setURI(builder.build());
      }

    } catch (Exception exception) {
      exception.printStackTrace();

    }
    if (body != null) {
      HttpEntity entity = new StringEntity(body,
            ContentType.APPLICATION_JSON);
      delete.setEntity(entity);
    }
    HttpEntity entity = null;
    try {
      HttpResponse response = client.execute(delete);
      int statusCode = response.getStatusLine().getStatusCode() / 100;
      entity = response.getEntity();
      String result = EntityUtils.toString(response.getEntity());
      if (statusCode == 4 || statusCode == 5) {
        throw new IOException(result);
      }

      if (String.class.equals(clazz)) {
        return (T) result;
      }
      if (result == null || result.length() == 0) {
        return null;
      }

      return JsonUtil.fromJson(clazz, result);
    } finally {
      if (entity != null) {
        EntityUtils.consume(entity);
      }
    }
  }

  private static class DpHttpDelete extends HttpEntityEnclosingRequestBase {

    static final String METHOD_NAME = "DELETE";

    DpHttpDelete(final String uri) {
      super();
      setURI(URI.create(uri));
    }

    public DpHttpDelete(final URI uri) {
      super();
      setURI(uri);
    }

    public DpHttpDelete() {
      super();
    }

    public String getMethod() {
      return METHOD_NAME;
    }
  }

}